
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <title>NESASM</title>
    <style>
    html,body{font-size:14px; color:#444;} 
    a {text-decoration: none; color:blue;}
    a:hover{color:red;}
    .main{margin:0 auto; width:960px;}
    .box{border:1px solid #eee;}
    </style>
</head>

<body>

<div class="main">
<p>【主角DMA？】
</p><p>是的，主角DMA。你还记得我们怎么利用$2003和$2004寄存器写入SPR-RAM（OAM）的吗？实际上真实系统中这种做法是不可靠的。我们应该利用内存作为OAM，然后向一个寄存器写入值，所有内容都自动拷贝到真实OAM。如果你不懂，那我们做一遍就懂了。
</p><p>【什么内存？】
</p><p>就像我上面说的，我们需要使用“变量”内存来复制一份OAM。本教程使用$0300用于复制的OAM。注意NES上只有64个主角（占用64*4B=256B，0x100）。所以基本上你自己定义的变量尽可能放在$0000-$0200内。从$0300-$0400的布局应该同OAM完全一致。
</p><p>另外记住，$300是一个内存地址，而不像内存变量。我们必须在每次读写后将地址加一，而不是持续读写同一个地址的内容。
</p><p>我们用偶数在百位用于我们OAM拷贝。我会告诉你为什么的。
</p><p>好了，希望你理解了上述理论，下面看汇编代码。
</p><p>;;--- 代码开始 ---;;
</p><p>    .inesmap 0  ;
</p><p>    .inesprg 1  ; 
</p><p>    .ineschr 1  ; 
</p><p>    .inesmir 1  ; 
</p><p>    .bank 1
</p><p>    .org $FFFA
</p><p>    .dw 0
</p><p>    .dw Start
</p><p>    .dw 0
</p><p>    .bank 0  ; 代码段
</p><p>    .org $0000 ; 
</p><p>    ; 普通变量在这里定义
</p><p>    .org $0300 ; OAM镜像从这里开始
</p><p>Sprite1_Y:     .db  0   ; 1号主角的纵坐标
</p><p>Sprite1_T:     .db  0   ; 1号主角的瓷砖编号
</p><p>Sprite1_S:     .db  0   ; 1号主角的特殊待遇
</p><p>Sprite1_X:     .db  0   ; 1号主角的横坐标
</p><p>Sprite2_Y:     .db  0   ; 2号主角的纵坐标
</p><p>Sprite2_T:     .db  0   ; 不用说了吧？
</p><p>Sprite2_S:     .db  0   ; 
</p><p>Sprite2_X:     .db  0   ; 
</p><p>; 依此类推去吧。。。
</p><p>    .org $8000 ; 代码开始
</p><p>Start: 
</p><p>    ; 
</p><p>    ; 先卖个关子
</p><p>    ; 后面会给出详细代码
</p><p>infin:
</p><p>    jmp infin  ; 死循环
</p><p>;;--- 代码结束 ---;;
</p><p>如果你不懂，email我告诉我你到底哪不懂。
</p><p>【DMA寄存器】
</p><p>DMA寄存器地址是$4014，我们需要写3进去。为什么是3？因为我们内存OAM在$300处。你向$4014写入n，那么就从$0n00处拷贝内容到真正OAM。也就是说，如果从$0400开始那就写4，如果从$0500开始那就写5，明白？
</p><p>下面看怎么搬运我们的OAM到真实OAM中：
</p><p>    lda #$3  ; 也可以写成 #3, 因为显然3的十进制和十六进制表示是相同的
</p><p>    sta $4014 ; 一旦写入, 拷贝就开始
</p><p>就是这样，比我们老办法可靠多了，而且更简单！
</p><p>【怎样按照上述方法修改第九天的代码？】
</p><p>我们需要做几件事。
</p><p>首先，拷贝 .org $0300和后面那堆东西到我们旧的变量区。我们再也不用那些变量了，因为我们的主角X，Y都使用OAM拷贝。
</p><p>其次，使用查找替换功能吧所有X_Pos和Y_Pos改为Sprite1_X和Sprite1_Y。
</p><p>再次，找到写$2003的代码块，替换为：
</p><p>    lda #$3  ; 
</p><p>    sta $4014 ;
</p><p>就是它了！我们用这种方法也节省了几个字节代码空间。
</p><p>【今日回顾】
</p><p>希望你喜欢主角DMA，我已经将它尽可能口语化了。我们今天学到了更好的写OAM数据的方法，明天还要看下更好的方法来捕获VBlank。接下来是。。。。。中断！
</p><p>希望你的代码没有bug。
</p></div>
</body>
</html>
